package cn.bitkit.component.flow.executor;

import cn.bitkit.base.exception.BusinessAssert;
import cn.bitkit.component.flow.common.*;
import cn.bitkit.component.context.BizServiceContext;
import cn.bitkit.base.util.StringUtil;
import cn.bitkit.component.context.TraceContext;
import cn.bitkit.tools.data.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 流程链执行器
 * @author changbo
 */
@Slf4j
public class FlowAsyncExecutor {

    @Autowired
    BaseFlowSender baseFlowSender;
    @Autowired
    BaseFlowConfig baseFlowConfig;

    @Async
    public void execute(FlowData data){
        if(StringUtil.isEmpty(data.getTrace())){
            data.setTrace(TraceContext.TRACE_ID.get());
        }
        BusinessAssert.isNotEmpty(data.getType(), "type can't be null");
        BusinessAssert.isNotEmpty(data.getServiceCode(), "serviceCode can't be null");
        List<FlowNode> list = baseFlowConfig.getNodeList(data.getType());
        BusinessAssert.isNotEmpty(list, "serviceCode not found in FlowChainNode:"+data.getServiceCode());
        FlowNode flowNode = list.getFirst();
        log.info("[{}][orderNo={}]run start:{}", data.getServiceCode(), data.getOrderNo(), JSONUtil.toJson(data));
        BaseFlowService baseFlowService = BizServiceContext.getBizService(data.getServiceCode());
        BusinessAssert.isNotNull(baseFlowService, "serviceCode not found in FlowChainNode:"+data.getServiceCode());
        FlowStatus status = null;
        try {
            data.setStartTime(LocalDateTime.now());
            status = baseFlowService.execute(data);
        }catch (Exception e){
            log.error("[{}][orderNo={}]run error:", data.getServiceCode(), data.getOrderNo(), e);
        }
        data.setEndTime(LocalDateTime.now());
        data.setExecuteCount(data.getExecuteCount()+1);
        if(status == null){
            status = FlowStatus.fail;
        }
        log.info("[{}][orderNo={}]run end[status={}]:{}", data.getServiceCode(), data.getOrderNo(), status.code, JSONUtil.toJson(data));
        baseFlowConfig.save(data);
        if(FlowStatus.finish.equals(status)){
            return;
        }
        if(FlowStatus.retry.equals(status)){
            data.setStartTime(null);
            data.setEndTime(null);
            baseFlowSender.send(data, true);
            return;
        }
        List<String> nextList = null;
        if(FlowStatus.success.equals(status)){
            nextList = flowNode.getSuccessList();
        }
        if(FlowStatus.fail.equals(status)){
            nextList = flowNode.getFailList();
        }
        if(nextList == null || nextList.isEmpty()){
            return;
        }
        nextList.forEach(next -> {
            if(StringUtil.isEmpty(next)){
                return;
            }
            FlowData nextData = FlowData.builder()
                    .trace(data.getTrace())
                    .orderNo(data.getOrderNo())
                    .data(data.getData())
                    .type(data.getType())
                    .serviceCode(next)
                    .build();
            log.info("[{}][orderNo={}]next service:{}", data.getServiceCode(), data.getOrderNo(), next);
            baseFlowSender.send(data, false);
        });

    }


}
